package com.mq.data.base.impl;

import java.io.Serializable;
import java.util.*;
import com.mq.common.entity.EntiytList;
import com.mq.common.entity.page.EntryPage;
import com.mq.common.entity.page.Page;
import com.mq.common.exception.BussinessException;
import com.mq.common.exception.code.ErrorCode;
import com.mq.common.pagehelper.PageHelper;
import com.mq.common.utils.FiledUtils;
import com.mq.common.utils.ReflectionUtils;
import com.mq.data.base.BaseMapper;
import com.mq.data.base.IBaseRepository;
import com.mq.data.base.bean.RepositoryMeta;
import com.mq.data.base.utils.TableHelper;
import com.mq.utils.ExceptionFormatUtil;
import com.mq.utils.GsonHelper;
import com.mq.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

/**
 * 基础数据仓库
 *
 * @param <E> 实体bean class
 * @param <M> mapper类class
 */
@Slf4j
public class BaseRepository<E, M> implements IBaseRepository<E> {

    private volatile  BaseMapper baseMapper;

    /**
     * 服务类对应的元数据
     */
    private RepositoryMeta repositoryMeta = new RepositoryMeta() {
        {
            BaseRepository<E, M> srv = BaseRepository.this;
            Class<? extends BaseRepository> clazzType = srv.getClass();
            Class<E> etClassType = ReflectionUtils.getSuperClassGenricType(clazzType);
            Class<M> mtClassType = ReflectionUtils.getSuperClassGenricType(clazzType, 1);
            setEtClassType(etClassType);
            setMtClassType(mtClassType);
        }
    };

    @Override
    @Transactional(rollbackFor = Exception.class)
    public E createItem(E record) throws BussinessException {
        try {
            getBaseMapper().insertSelective(record);
            return record;
        } catch (Exception e) {
            log.error("[createItem],record={},error={}", record, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_CREATE_ERROR, e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public E createItemAllColumn(E record) throws BussinessException {
        try {
            getBaseMapper().insert(record);
            return record;
        } catch (Exception e) {
            log.error("[createItemAllColumn],record={},error={}", record, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_CREATE_ERROR, e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean insertBatch(List<E> records) throws BussinessException {
        try {
            for (E record : records) {
                getBaseMapper().insertSelective(record);
            }
        } catch (Exception e) {
            log.error("[insertBatch],record={},error={}", records, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_CREATE_ERROR, e);
        }
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public E insertOrUpdate(E record) throws BussinessException {
        try {
            Object tablePrimaryKey = TableHelper.getTablePrimaryKey(record);
            //主键值为空：新增
            if (null == tablePrimaryKey) {
                return createItem(record);
            } else {
                //更新
                int rs = updateById(record);
                if (rs <= 0) {
                    return createItem(record);
                }
                return record;
            }
        } catch (Exception e) {
            log.error("[insertOrUpdate],record={},error={}", record, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_CREATE_ERROR, e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public E insertOrUpdateAllColumn(E record) throws BussinessException {
        try {
            Object tablePrimaryKey = TableHelper.getTablePrimaryKey(record);
            //主键值为空：新增
            if (null == tablePrimaryKey) {
                return createItemAllColumn(record);
            } else {
                //更新
                int r = updateAllColumnById(record);
                if (r <= 0) {
                    //更新失败则新建
                    return createItemAllColumn(record);
                }
                return record;
            }
        } catch (Exception e) {
            log.error("[insertOrUpdateAllColumn],record={},error={}", record, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_CREATE_ERROR, e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteById(Serializable id) throws BussinessException {

        try {
            return getBaseMapper().deleteByPrimaryKey(id);
        } catch (Exception e) {
            log.error("[deleteById],record={},error={}", id, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_DELETE_ERROR, e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByIds(Collection<? extends Serializable> ids) throws BussinessException {
        try {
            return getBaseMapper().deleteByIds(ids);
        } catch (Exception e) {
            log.error("[deleteByIds],record={},error={}", ids, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_DELETE_ERROR, e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateById(E record) throws BussinessException {
        try {
            return getBaseMapper().updateByPrimaryKeySelective(record);
        } catch (Exception e) {
            log.error("[updateById],record={},error={}", record, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_UPDATE_ERROR, e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateAllColumnById(E record) throws BussinessException {
        try {
            return getBaseMapper().updateByPrimaryKey(record);
        } catch (Exception e) {
            log.error("[updateAllColumnById],record={},error={}", record, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_UPDATE_ERROR, e);
        }
    }

    @Override
    public boolean exists(Map<String, Object> params) throws BussinessException {
        try {
            return countByMap(params) > 0;
        } catch (Exception e) {
            log.error("[exists],record={},error={}", params, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }
    }

    @Override
    public E retrieveItemById(Serializable id) throws BussinessException {
        try {
            return (E) getBaseMapper().selectByPrimaryKey(id);
        } catch (Exception e) {
            log.error("[retrieveItemById],record={},error={}", id, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }
    }

    @Override
    public List<E> retrieveListByIds(Collection<? extends Serializable> ids) throws BussinessException {
        try {
            return getBaseMapper().selectBatchIds(ids);
        } catch (Exception e) {
            log.error("[retrieveListByIds],record={},error={}", ids, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }
    }

    @Override
    public List<E> retrieveListByItem(E record) throws BussinessException {
        try {
            return retrieveListByMap(FiledUtils.transfer2Map(record));
        } catch (Exception e) {
            log.error("[retrieveListByItem],record={},error={}", record, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }
    }

    @Override
    public List<E> selectAll() throws BussinessException {
        try {
            return getBaseMapper().selectListByMap(null);
        } catch (Exception e) {
            log.error("[selectAll],error={}", ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }
    }

    @Override
    public List<E> retrieveListByMap(Map map) throws BussinessException {
        try {
            return getBaseMapper().selectListByMap(map);
        } catch (Exception e) {
            log.error("[retrieveListByMap],record={},error={}", map, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }
    }

    @Override
    public EntiytList<E> retrieveListByPage(EntryPage page) throws BussinessException {
        try {
            Map<String, Object> params = page.getMap();
            Class<E> clazzType = ReflectionUtils.getSuperClassGenricType(this.getClass());
            E paramsBean = GsonHelper.fromJson(GsonHelper.toJson(params), clazzType);
            return retrieveListPageByItem(paramsBean, page.getPageNum(), page.getPageSize());
        } catch (Exception e) {
            log.error("[retrieveListByPage],record={},error={}", page, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }
    }

    @Override
    public EntiytList<E> retrieveListPageByItem(E record, Integer pageNum, Integer pageSize) throws BussinessException {
        try {
            int pNum = pageNum == null ? 1 : pageNum.intValue();
            int pSize = pageNum == null ? 20 : pageSize.intValue();
            PageHelper.startPage(pNum, pSize);
            Map<String, Object> params = FiledUtils.transfer2Map(record);
            Page<E> page = (Page<E>) getBaseMapper().selectListByMap(params);
            EntiytList etEntryList = new EntiytList(page);
            page.getPage(etEntryList);
            return etEntryList;
        } catch (Exception e) {
            log.error("[retrieveListPageByItem],record={},error={}", record, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }
    }

    @Override
    public int countAll() throws BussinessException {
        try {
            return countByMap(null);
        } catch (Exception e) {
            log.error("[countAll],error={}", ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }

    }

    @Override
    public int countByMap(Map<String, Object> params) throws BussinessException {
        try {
            return getBaseMapper().countByMap(params);
        } catch (Exception e) {
            log.error("[countByMap],record={},error={}", params, ExceptionFormatUtil.getTrace(e, 8));
            throw new BussinessException(ErrorCode.DB_QUERY_ERROR, e);
        }
    }

    /**
     * 获取mapper实例
     *
     * @return
     */
    private BaseMapper getBaseMapper() {
        if (null == baseMapper) {
            Class<? extends BaseRepository> srv = this.getClass();
            String key = srv.getName();
            synchronized (key) {
                if(null ==baseMapper){
                    Class<M> mtClassType = repositoryMeta.getMtClassType();
                    //从容器获取
                    baseMapper = (BaseMapper) SpringUtils.getBean(mtClassType);
                }
            }
        }
        return baseMapper;
    }

}
